package com.sntra.beibi.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.stereotype.Service;

import com.itextpdf.kernel.colors.WebColors;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.sntra.beibi.common.Bezier;
import com.sntra.beibi.common.ControllerPoint;
import com.sntra.beibi.common.DotUtils;
import com.sntra.beibi.domain.Dot;
import com.sntra.beibi.service.PenService;

/**
 * @author ljm 带笔锋的绘制
 * @date 2021/7/27.
*/
@Service 
public class StrokePenServiceImpl implements PenService  {
    private static final String TAG = "StrokePen";
    /**
     * 笔迹颜色
     */
    private String penColor = "#000000";
    /**
     * 笔迹宽度
     */
    private float penWidth = 1.0f;
    public List<ControllerPoint> mHWPointList = Collections.synchronizedList(new ArrayList<ControllerPoint>());
    /**
     * 计算出来的线段宽度
     */
    public double mLastWidth;
    public ControllerPoint mLastPoint = new ControllerPoint(0, 0);

    private ControllerPoint mCurPoint;
    private Bezier mBezier = new Bezier();
    /**
     * 转换比率
     */
    private float TRANSFORM_SCALE = 40.00f;
     
    @Override
	public void onDown(float pointX, float pointY, int force) {
        //Log.i(TAG, "onDown: pointX=" + pointX + "/pointY" + pointY + "/force" + force + "/penWidth" + penWidth);
        float pressure = calculatePressure(force);

        mHWPointList.clear();
        //记录down的控制点的信息
        ControllerPoint curPoint = new ControllerPoint(pointX, pointY);
        //如果用笔画的画我的屏幕，记录他宽度的和压力值的乘
        mLastWidth = (float) pressure / TRANSFORM_SCALE * penWidth;
        //down下的点的宽度
        curPoint.width = (float) mLastWidth;
        mHWPointList.add(curPoint);
        //记录当前的点
        mLastPoint = curPoint;
    }

    @Override
	public void onMove(float pointX, float pointY, int force) {
        //Log.i(TAG, "onDown: pointX=" + pointX + "/pointY" + pointY + "/force" + force + "/penWidth" + penWidth);
        ControllerPoint curPoint = new ControllerPoint(pointX, pointY);
        float pressure = calculatePressure(force);
        double curWidth = (float) pressure / TRANSFORM_SCALE * penWidth;
        curPoint.width = (float) curWidth;
        double deltaX = curPoint.x - mLastPoint.x;
        double deltaY = curPoint.y - mLastPoint.y;
        //deltaX和deltay平方和的二次方根 想象一个例子 1+1的平方根为1.4 （x²+y²）开根号
        //同理，当滑动的越快的话，deltaX+deltaY的值越大，这个越大的话，curDis也越大
        double curDis = Math.hypot(deltaX, deltaY);
        //点的集合少，我们得必须改变宽度,每次点击的down的时候，这个事件
        if (mHWPointList.size() < 2) {
            mBezier.init(mLastPoint, curPoint);
        } else {
            mBezier.addNode(curPoint);
        }
        //每次移动的话，这里赋值新的值
        mLastWidth = curWidth;
        doMove(curDis);
        mLastPoint = curPoint;
    }

    @Override
	public void onUp(float pointX, float pointY, int force, PdfCanvas canvas) {
        //Log.i(TAG, "onMove: pointX=" + pointX + "/pointY" + pointY);
        if (mHWPointList.size() == 0) {
            return;
        }
        mCurPoint = new ControllerPoint(pointX, pointY);
        double deltaX = mCurPoint.x - mLastPoint.x;
        double deltaY = mCurPoint.y - mLastPoint.y;
        double curDis = Math.hypot(deltaX, deltaY);
        mCurPoint.width = 0;
        mBezier.addNode(mCurPoint);
        int steps = 1 + (int) curDis / 10;
        double step = 1.0 / steps;
        for (double t = 0; t < 1.0; t += step) {
            ControllerPoint point = mBezier.getPoint(t);
            mHWPointList.add(point);
        }
        mBezier.end();
        for (double t = 0; t < 1.0; t += step) {
            ControllerPoint point = mBezier.getPoint(t);
            mHWPointList.add(point);
        }
        draws(canvas);
        // 在onDown下去的话，最近画的线的透明度有改变，所以这里clear下线的集合
        clear();
    }

    public void draws(PdfCanvas canvas) {
        //点的集合少 不去绘制
        if (mHWPointList == null || mHWPointList.size() < 1) {
            return;
        }
        doPreDraw(canvas);
    }

    private synchronized void doPreDraw(PdfCanvas canvas) {
        mCurPoint = mHWPointList.get(0);
        int size = mHWPointList.size();
        for (int i = 1; i < size; i++) {
            ControllerPoint point = mHWPointList.get(i);
            drawToPoint(point, canvas);
            mCurPoint = point;
        }
    }

    public void clear() {
        mHWPointList.clear();
    }


    private void doMove(double curDis) {
        int steps = 1 + (int) curDis / 10;
        double step = 1.0 / steps;
        for (double t = 0; t < 1.0; t += step) {
            ControllerPoint point = mBezier.getPoint(t);
            mHWPointList.add(point);
        }
    }

    private void drawToPoint(ControllerPoint point, PdfCanvas canvas) {
        if ((mCurPoint.x == point.x) && (mCurPoint.y == point.y)) {
            return;
        }
        doDraw(point, canvas);
    }

    private void doDraw(ControllerPoint point, PdfCanvas canvas) {
        drawLine( mCurPoint.x, mCurPoint.y, mCurPoint.width, point.x,
                point.y, point.width, canvas);
    }


    private void drawLine(double x0, double y0, double w0, double x1, double y1, double w1, PdfCanvas canvas) {
        //求两个数字的平方根 x的平方+y的平方在开方记得X的平方+y的平方=1，这就是一个园
        double curDis = Math.hypot(x0 - x1, y0 - y1);
        int steps = 1;
        if (penWidth <= 6) {
            steps = 1 + (int) (curDis);
        } else if (penWidth > 6) {
            steps = 1 + (int) (curDis/4);
        } else {
            steps = 1 + (int) (curDis/3);
        }
        canvas.setStrokeColor(WebColors.getRGBColor(penColor));
        
        canvas.setLineWidth(penWidth);
       
        double deltaX = (x1 - x0) / steps;
        double deltaY = (y1 - y0) / steps;
        double deltaW = (w1 - w0) / steps;
        double x = x0;
        double y = y0;
        double w = w0;
        for (int i = 0; i < steps; i++) {
            //都是用于表示坐标系中的一块矩形区域，并可以对其做一些简单操作
            Double left, top, right, bottom ,width,height;
            left = (double) (x + w / 2.0d);
            top = (double) (y + w / 2.0d);
            right = (double) (x - w / 2.0d);
            bottom = (double) (y - w / 2.0d);
            BigDecimal lb = new  BigDecimal(left.toString());
            BigDecimal tb = new  BigDecimal(top.toString());
            BigDecimal rn = new BigDecimal(Double.toString(right)); 
            BigDecimal bb = new BigDecimal(Double.toString(bottom)); 

            if(right>left) {
            	width = rn.subtract(lb).doubleValue();
            }else {
            	width = lb.subtract(rn).doubleValue();
            	left = right;
            }
            if(top<bottom) {
            	height = bb.subtract(tb).doubleValue();
            }else {
            	height = tb.subtract(bb).doubleValue();
            	top = bottom;
            }
            //最基本的实现，通过点控制线，绘制椭圆
            //g2d.drawOval(left, top, wh, he);
            canvas.roundRectangle(left, top, width , height , penWidth).fillStroke();
            x += deltaX;
            y += deltaY;
            w += deltaW;
        }
    }

    /**
     * 区分压力值
     * @param force
     * @return
     */
    private int calculatePressure(int force) {
        int pressure = 1;
        if (force >= 0 && force <= 20) {
            pressure = 40;
        } else if (force > 20 && force <= 40) {
            pressure = 60;
        } else if (force > 40 && force <= 60) {
            pressure = 80;
        } else if (force > 60 && force <= 90) {
            pressure = 100;
        } else if (force > 90 && force <= 150) {
            pressure = 120;
        } else {
            pressure = 130;
        }
        return pressure;
    }
    
    /**
	 * 绘制数据
	 *
	 * @param dot
	 */
	public void processDot(Dot dot, int bgSize, double pageSize, PdfCanvas canvas,float width,float height) {
		
		float x = DotUtils.joiningTogether(dot.getX(), dot.getFx());
		float y = DotUtils.joiningTogether(dot.getY(), dot.getFy());
		float pointX, pointY;

		pointX = DotUtils.getPoint(x, (int)bgSize, pageSize, DotUtils.DIST_PERUNIT);
		pointY = DotUtils.getPoint(y, (int)bgSize, pageSize, DotUtils.DIST_PERUNIT);

		switch (dot.getType()) {
		case PEN_DOWN:
			onDown(pointX, height - pointY, dot.getForce());
			break;
		case PEN_MOVE:
			onMove(pointX, height - pointY, dot.getForce());
			break;
		case PEN_UP:
			onUp(pointX, height - pointY, 1, canvas);
			break;
		}
	}
	
	 /**
     * 根据像素密度动态计算笔迹宽度
     *
     * @param width
     * @return
     */
    public float transformWidth(float width, float dpi) {
        float density = dpi/160f;
        density = width * density * 0.5f ;
        setPenWidth(density);
        return density;
    }

	public void setPenWidth(float penWidth) {
		this.penWidth = penWidth;
	}
}
